﻿using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows.Documents;
using FarmerJoe.Helpers.Logging;
using Styx;
using Styx.CommonBot;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;

namespace FarmerJoe.Helpers
{
    namespace Units
    {
        static class Target
        {
            private static LocalPlayer Me { get { return StyxWoW.Me; } }
            private static WoWUnit CT { get { return Me.CurrentTarget; } }

            public static bool IsLevelAppropriate(this WoWUnit unit, int levelRange)
            {
                // This is a very crude way to determine if a target is considered level appropriate
                // Immediately, if the target is higher level than you its 'level appropriate'
                // Take our level, subtract the 'levelRange' and compare it with the target's level
                // If its below or equal then the target is considered 'level appropriate'

                // Eg You are level 85. The target is level 80. 'levelRange' is 7
                // 85-7 = 78. 80 > 78 so its 'level appropriate'
                // It would be considered level appropriate is the target was as low as 78.

                if (unit.Level >= Me.Level) return true;
                if (!unit.IsValid) return false;
                if (unit.Level < (Me.Level - levelRange)) return false;
                if (unit.Elite) return true;

                return true;
            }

            public static bool IsValidTarget(this WoWUnit p)
            {
                if (!p.CanSelect || !p.Attackable) return false;
                if (p.IsFriendly) return false;
                if (p.IsDead) return false;
                if (p.IsPet && p.OwnedByUnit.IsPlayer || p.OwnedByRoot != null) return false;
                if (p.IsNonCombatPet || p.IsCritter) return false;

                if (p.Name == "Training Dummy") return true;

                return true;
            }

            public static void ChooseTarget()
            {
                // If we have a valid target bail out here
                if (Me.GotTarget && Me.CurrentTarget.IsValidTarget()) return;

                // Take your pet's target
                if (Me.GotAlivePet && Me.Pet.GotTarget && (!Me.GotTarget || !Me.CurrentTarget.Combat))
                {
                    Me.Pet.CurrentTarget.Target();
                    Thread.Sleep(250);
                    return;
                }
                
                // Find a suitable target
                foreach (WoWUnit unit in ObjectManager.GetObjectsOfType<WoWUnit>(false, false))
                {
                    if (!unit.Combat) continue;
                    if (!unit.IsValidTarget()) continue;
                    if (unit.Distance2DSqr > 60 * 60) continue;
                    if (!unit.Aggro || (Me.GotAlivePet && !unit.PetAggro)) continue;

                    unit.Target();
                }
            }

            public static bool HasDebuff(this WoWUnit unit, string debuff)
            {
                return CT.HasAura(debuff);
            }

            /*
            public static string SafeName(this WoWObject obj)
            {
                string name;
                if (obj.IsMe) { return "myself"; }


                if (obj is WoWPlayer)
                {
                    if (RaFHelper.Leader == obj) return "Tank";
                    name = ((WoWPlayer)obj).Class.ToString();
                }
                else if (obj is WoWUnit && obj.ToUnit().IsPet)
                {
                    name = "Pet";
                }
                else
                {
                    name = obj.Name;
                }

                return name;
            }
             */

            public static bool IsLowLevel(this WoWUnit unit)
            {
                return unit.Level <= Me.Level - 4;
            }

            public static bool IsHighLevel(this WoWUnit unit)
            {
                return unit.Level >= Me.Level + 3 || unit.Elite;
            }
        }

        static class Self
        {
             
        }

        static class AddsAndOthers
        {
            private static LocalPlayer Me { get { return StyxWoW.Me; } }
            private static WoWUnit CT { get { return Me.CurrentTarget; } }
           


            public static bool HasAdds
            {
                get
                {
                    List<WoWUnit> units = (from u in ObjectManager.GetObjectsOfType<WoWUnit>(false, false)
                                           where u.Distance2DSqr <= 40 * 40
                                           where u.IsValidTarget()
                                           where u.IsTargetingAnyMinion || u.IsTargetingMeOrPet || u.IsTargetingMyPartyMember || u.IsTargetingMyRaidMember
                                           select u).ToList();

                    return units.Count > 1;
                }
            }

            public static int AddCount
            {
                get
                {
                    List<WoWUnit> units = (from u in ObjectManager.GetObjectsOfType<WoWUnit>(false, false)
                                           where u.Distance2DSqr <= 40 * 40
                                           where u.IsValidTarget()
                                           where u.IsTargetingAnyMinion || u.IsTargetingMeOrPet || u.IsTargetingMyPartyMember || u.IsTargetingMyRaidMember
                                           select u).ToList();

                    return units.Count;
                }
            }

            public static List<WoWUnit>MobsAttackingUnit(WoWUnit unit)
            {
                List<WoWUnit> units = new List<WoWUnit>();

                foreach (WoWUnit u in ObjectManager.GetObjectsOfType<WoWUnit>(false, false))
                {
                    if (u.Distance2DSqr > 40*40) continue;
                    if (!u.Combat) continue;
                    if (!u.Attackable) continue;
                    if (!u.GotTarget) continue;
                    if (u.CurrentTargetGuid == unit.Guid)
                    {
                        units.Add(u);
                    }

                }

                return units;
            }

            public static IEnumerable<WoWPlayer> NearbyFriendlyPlayers
            {
                get
                {
                    return ObjectManager.GetObjectsOfType<WoWPlayer>(true, true).Where(p => p.DistanceSqr <= 40 * 40 && p.IsFriendly).ToList();
                }
            }

            public static bool IsAnyPVETargetingMe
            {
                get
                {
                    foreach (WoWUnit unit in ObjectManager.GetObjectsOfType<WoWUnit>(false,false))
                    {
                        if (unit.Distance2DSqr > 50 * 50) continue;
                        if (unit.IsTargetingPet) continue;
                        if (unit.IsCrowdControlled()) continue;
                        if (!unit.IsHostile) continue;
                        if (unit.IsPlayer) continue;
                        if (unit.GotTarget && unit.CurrentTargetGuid == Me.Guid) return true;

                    }
                    return false;
                }
            }

            public static bool IsAnyPlayerTargetingMe
            {
                get
                {
                    foreach (WoWUnit unit in ObjectManager.GetObjectsOfType<WoWUnit>(false, false))
                    {
                        if (unit.Distance2DSqr > 41 * 41) continue;
                        if (!unit.IsPlayer) continue;
                        if (unit.IsTargetingPet) continue;
                        if (!unit.GotTarget) continue;
                        //if (unit.IsCrowdControlled()) continue;

                        if (unit.GotTarget && unit.CurrentTargetGuid == Me.Guid) return true;
                        

                    }
                    return false;
                }
            }

            public static bool HasHostilePlayersInRange(double distance)
            {
                {
                    foreach (WoWUnit unit in ObjectManager.GetObjectsOfType<WoWUnit>(false, false))
                    {
                        if (unit.Distance2DSqr > distance * distance) continue;
                        if (!unit.IsPlayer) continue;
                        if (unit.IsCrowdControlled()) continue;

                        return true;
                        //if (unit.GotTarget && unit.CurrentTargetGuid == Me.Guid) return true;

                    }
                    return false;
                }
            }


            public static IEnumerable<WoWUnit> AggroUnitsNearTarget(this WoWUnit unit, float distance)
            {
                return AggroUnitsNearTarget(unit, distance,false);
            }

            public static IEnumerable<WoWUnit> AggroUnitsNearTarget(this WoWUnit unit, float distance, bool excludeCurrentTarget)
            {
                float dist = distance * distance;
                //WoWPoint curTarLocation = StyxWoW.Me.CurrentTarget.Location;
                WoWPoint curTarLocation = unit.Location;

                List<WoWUnit> listOfMobs = new List<WoWUnit>();
                foreach (WoWUnit u in ObjectManager.GetObjectsOfType<WoWUnit>(false, false))
                {
                    if (u == null) continue;
                    if (!u.IsValidTarget()) continue;
                    if (u.Location.Distance2DSqr(curTarLocation) > dist) continue;
                    if (!u.Aggro && !u.PetAggro) continue;
                    if (excludeCurrentTarget && u.Guid == StyxWoW.Me.CurrentTargetGuid) continue;

                    // Your current target is counted. So if you have 1 add this will return 2.
                    // Uncomment the below line will return the current count LESS your current target.
                    //if (u.Guid == StyxWoW.Me.CurrentTargetGuid) continue;

                    listOfMobs.Add(u);
                }

                return listOfMobs;
            }

            public static IEnumerable<WoWUnit> NearbyUnfriendlyUnits
            {
                get { return ObjectManager.GetObjectsOfType<WoWUnit>(false, false).Where(p => Target.IsValidTarget(p) && p.DistanceSqr <= 40 * 40).ToList(); }
            }

            public static IEnumerable<WoWUnit> HostileUnitsNearTarget(this WoWUnit unit, float distance)
            {
                float dist = distance * distance;
                WoWPoint curTarLocation = StyxWoW.Me.GotTarget ? StyxWoW.Me.CurrentTarget.Location : StyxWoW.Me.Location;

                List<WoWUnit> listOfMobs = new List<WoWUnit>();
                foreach (WoWUnit u in ObjectManager.GetObjectsOfType<WoWUnit>(false, false))
                {
                    if (!u.IsValidTarget()) continue;
                    if (u.Location.Distance2DSqr(curTarLocation) > dist) continue;
                    if (!u.IsHostile) continue;
                    
                    // Your current target is counted. So if you have 1 add this will return 2.
                    // Uncomment the below line will return the current count LESS your current target.
                    //if (u.Guid == StyxWoW.Me.CurrentTargetGuid) continue;
                    //Log.Write("=========== found hostile in range " + u.Name + " distance of " + u.Distance);

                    listOfMobs.Add(u);
                }

                return listOfMobs;//ObjectManager.GetObjectsOfType<WoWUnit>(false, false).Where( p => p.IsValidTarget() && p.Location.DistanceSqr(curTarLocation) <= dist).ToList();
            }

            public static bool IsCrowdControlledUnitNearTarget(this WoWUnit unit, float distance)
            {
                float dist = distance * distance;
                List<WoWUnit> listOfMobs = new List<WoWUnit>();
                WoWPoint curTarLocation = StyxWoW.Me.CurrentTarget.Location;

                foreach (WoWUnit u in ObjectManager.GetObjectsOfType<WoWUnit>(false, false))
                {
                    if (!u.IsValidTarget()) continue;
                    if (u.Location.Distance2DSqr(curTarLocation) > dist) continue;
                    if (!u.IsCrowdControlled()) continue;
                    if (u.Guid == StyxWoW.Me.CurrentTargetGuid) continue;

                    /*
                    Logging.Log.Write("There is a CC'd mob in range.");
                    Log.Write("name " + u.Name);
                    Log.Write("my target ? " + (u.Guid == StyxWoW.Me.CurrentTargetGuid));
                     */
                    return true;
                }

                //listOfMobs = ObjectManager.GetObjectsOfType<WoWUnit>(false, false).Where(p => p.IsValidTarget() && p.Location.DistanceSqr(curTarLocation) <= dist).ToList();
                return false;
            }

            public static IEnumerable<WoWUnit> CrowdControlledUnitsNearTarget(this WoWUnit unit, float distance)
            {
                float dist = distance * distance;
                List<WoWUnit> listOfMobs = new List<WoWUnit>();
                WoWPoint curTarLocation = StyxWoW.Me.CurrentTarget.Location;

                foreach (WoWUnit u in ObjectManager.GetObjectsOfType<WoWUnit>(false,false))
                {
                    if (!u.IsValidTarget()) continue;
                    if (u.Location.Distance2DSqr(curTarLocation) > dist) continue;
                    if (!u.IsCrowdControlled()) continue;
                    if (u.Guid == StyxWoW.Me.CurrentTargetGuid) continue;

                    listOfMobs.Add(u);
                }

                //listOfMobs = ObjectManager.GetObjectsOfType<WoWUnit>(false, false).Where(p => p.IsValidTarget() && p.Location.DistanceSqr(curTarLocation) <= dist).ToList();
                return listOfMobs;
            }

            public static bool IsCrowdControlled(this WoWUnit unit)
            {
                Dictionary<string, WoWAura>.ValueCollection auras = unit.Auras.Values;

                return auras.Any(
                    a => a.Spell.Mechanic == WoWSpellMechanic.Banished ||
                         a.Spell.Mechanic == WoWSpellMechanic.Charmed ||
                         a.Spell.Mechanic == WoWSpellMechanic.Horrified ||
                         a.Spell.Mechanic == WoWSpellMechanic.Incapacitated ||
                         a.Spell.Mechanic == WoWSpellMechanic.Polymorphed ||
                         a.Spell.Mechanic == WoWSpellMechanic.Sapped ||
                         a.Spell.Mechanic == WoWSpellMechanic.Shackled ||
                         a.Spell.Mechanic == WoWSpellMechanic.Asleep ||
                         a.Spell.Mechanic == WoWSpellMechanic.Frozen ||
                         a.Spell.Mechanic == WoWSpellMechanic.Invulnerable ||
                         a.Spell.Mechanic == WoWSpellMechanic.Invulnerable2 ||
                         a.Spell.Mechanic == WoWSpellMechanic.Turned ||
                         a.Spell.Mechanic == WoWSpellMechanic.Disoriented ||

                         // Really want to ignore hexed mobs.
                         a.Spell.Name == "Hex"

                         );
            }
        }

        /*
        static class Pets
        {
            private static LocalPlayer Me { get { return StyxWoW.Me; } }
            private static readonly List<WoWPetSpell> PetSpells = new List<WoWPetSpell>();
            private static ulong _petGuid;

            public static void CastPetAction(string action)
            {
                UpdatePetSpells();
                WoWPetSpell spell = PetSpells.FirstOrDefault(p => p.ToString() == action);
                if (spell == null) return;

                Log.Write(string.Format("[FJ Pet] Casting {0}", action));
                Lua.DoString("CastPetAction({0})", spell.ActionBarIndex + 1);
            }

            public static void CastPetAction(string action, WoWUnit onUnit)
            {
                UpdatePetSpells();
                WoWPetSpell spell = PetSpells.FirstOrDefault(p => p.ToString() == action);
                if (spell == null) return;

                Log.Write(string.Format("[FJ Pet] Casting {0} on {1}", action, onUnit.SafeName()));
                StyxWoW.Me.SetFocus(onUnit);
                Lua.DoString("CastPetAction({0}, 'focus')", spell.ActionBarIndex + 1);
                StyxWoW.Me.SetFocus(0);
            }

            private static void UpdatePetSpells()
            {
                if (!Timers.SpellOkToCast("PetSpells", 500)) return;
                if (!Me.GotAlivePet) return;
                if (_petGuid == Me.Pet.Guid) return;

                _petGuid = Me.Pet.Guid;
                PetSpells.Clear();
                PetSpells.AddRange(StyxWoW.Me.PetSpells);
                Timers.Reset("PetSpells");
                Log.Write("[FJ Pet] Pet spells have been updated");

            }

            public static bool CanCastPetAction(string action)
            {
                UpdatePetSpells();

                WoWPetSpell petAction = PetSpells.FirstOrDefault(p => p.ToString() == action);
                return petAction != null && petAction.Spell != null && !petAction.Spell.Cooldown;
            }
        }
        */
    }
}
